package br.ufc.great.dtx.tm.sch;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.Vector;

import br.ufc.great.Graph;
import br.ufc.great.dtx.server.DTXOutput;
import br.ufc.great.dtx.server.config.Config;
import br.ufc.great.dtx.tm.Transaction;
import br.ufc.great.dtx.tm.lm.LockManager;
import br.ufc.great.dtx.tm.operations.Operation;
import br.ufc.great.dtx.tm.operations.Site;

public class ProcessAbort extends Thread {

	private DTXOutput output;
	private String transactionId;
	private Scheduler scheduler;
	private LockManager lockManager;
	
	public ProcessAbort(DTXOutput output, Scheduler scheduler, LockManager lockManager, String transactionId) {
		this.output = output;
		this.transactionId = transactionId;
		this.scheduler = scheduler;
		this.lockManager = lockManager;
		setPriority(MIN_PRIORITY);
	}
	
	public void run() {
		Transaction transaction = scheduler.getTransaction(transactionId);
		if (transaction != null) {
			scheduler.removeTransaction(transaction);
			Vector<Operation> operations = transaction.getOperations();
			Vector<Site> sites = new Vector<Site>();
			for (int i = 0; i < operations.size(); i++) {
				Operation operation = operations.elementAt(i);
				Vector<Site> operationSites = operation.getSites();
				for (int j = 0; j < operationSites.size(); j++) {
					Site temp = operationSites.elementAt(j);
					boolean contain = false;
					for (int k = 0; k < sites.size(); k++) {
						Site sitesItem = sites.elementAt(k);
						if (sitesItem.getHost().equalsIgnoreCase(temp.getHost()) && sitesItem.getPort() == temp.getPort()) {
							contain = true;
							break;
						}
					}
					if (! contain)
						sites.add(temp);
				}
			}
			for (int j = 0; j < sites.size(); j++) {
				Site site = sites.elementAt(j);
				// Caso a operação seja remota
				if (! site.getHost().equalsIgnoreCase(Config.DTX_SERVER_HOST) || site.getPort() != Config.DTX_SERVER_PORT) {
					try {
						output.print(getClass().getSimpleName(), "Abort remote message " + transaction.getId() + " was sent to " + site.getHost() + ":" + site.getPort(), DTXOutput.TEST);
						Socket socket = new Socket(site.getHost(), site.getPort());
						ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
						Message message = new Message(transaction.getId(), Message.ABORT);
						out.writeObject(message);
						out.flush();
						ObjectInputStream in = new ObjectInputStream(socket.getInputStream());								
						message = (Message) in.readObject();
						switch (message.getStatus()) {
							case Message.EXECUTED : {
								output.print(getClass().getSimpleName(), "Abort remote message " + transaction.getId() + " on " + site.getHost() + ":" + site.getPort() + " was executed", DTXOutput.TEST);
								break;
							}
						}
						out.close();
						in.close();
						socket.close();
					}
					catch (IOException e) {
						output.print(getClass().getSimpleName(), e.getMessage(), DTXOutput.ERROR);
					}
					catch (ClassNotFoundException e) {
						output.print(getClass().getSimpleName(), e.getMessage(), DTXOutput.ERROR);
					}
				}
			}
			synchronized (lockManager) {
				lockManager.unlock(transaction.getId());								
			}
			Graph waitFor = scheduler.getWaitForGraph();
			synchronized (waitFor) {
				waitFor.removerVertex(transaction.getId());
			}
			transaction.abort();
			transaction.getThread().end();
		}
		else {
			synchronized (lockManager) {
				lockManager.unlock(transactionId);								
			}
			Graph waitFor = scheduler.getWaitForGraph();
			synchronized (waitFor) {
				waitFor.removerVertex(transactionId);
			}
		}
	}
	
}
